

#include <stdlib.h>
#include <string.h>
#include "flash_stm32.h"


#define SECTOR_MASK               ((uint32_t)0xFFFFFF07)



/**
  * @brief  Erases a specified FLASH Sector.
  *
  * @note   If an erase and a program operations are requested simultaneously,    
  *         the erase operation is performed before the program one.
  *
  * @param  FLASH_Sector: The Sector number to be erased.
  *
  *  @note  For STM32F405xx/407xx and STM32F415xx/417xx devices this parameter can 
  *         be a value between FLASH_Sector_0 and FLASH_Sector_11.
  *
  *         For STM32F42xxx/43xxx devices this parameter can be a value between 
  *         FLASH_Sector_0 and FLASH_Sector_23.
  *
  *         For STM32F401xx devices this parameter can be a value between 
  *         FLASH_Sector_0 and FLASH_Sector_5.
  *
  *         For STM32F411xE devices this parameter can be a value between 
  *         FLASH_Sector_0 and FLASH_Sector_7.
  *
  * @param  VoltageRange: The device voltage range which defines the erase parallelism.  
  *          This parameter can be one of the following values:
  *            @arg VoltageRange_1: when the device voltage range is 1.8V to 2.1V, 
  *                                  the operation will be done by byte (8-bit) 
  *            @arg VoltageRange_2: when the device voltage range is 2.1V to 2.7V,
  *                                  the operation will be done by half word (16-bit)
  *            @arg VoltageRange_3: when the device voltage range is 2.7V to 3.6V,
  *                                  the operation will be done by word (32-bit)
  *            @arg VoltageRange_4: when the device voltage range is 2.7V to 3.6V + External Vpp, 
  *                                  the operation will be done by double word (64-bit)
  *       
  * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PROGRAM,
  *                       FLASH_ERROR_WRP, FLASH_ERROR_OPERATION or FLASH_COMPLETE.
  */
FLASH_Status FLASH_EraseSector(uint32_t FLASH_Sector, uint8_t VoltageRange)
{
  uint32_t tmp_psize = 0x0;
  FLASH_Status status = FLASH_COMPLETE;

  /* Check the parameters */
  if (IS_FLASH_SECTOR(FLASH_Sector) && IS_VOLTAGERANGE(VoltageRange))
  {
    if(VoltageRange == VoltageRange_1)
    {
       tmp_psize = FLASH_PSIZE_BYTE;
    }
    else if(VoltageRange == VoltageRange_2)
    {
      tmp_psize = FLASH_PSIZE_HALF_WORD;
    }
    else if(VoltageRange == VoltageRange_3)
    {
      tmp_psize = FLASH_PSIZE_WORD;
    }
    else
    {
      tmp_psize = FLASH_PSIZE_DOUBLE_WORD;
    }
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation();
    
    if(status == FLASH_COMPLETE)
    { 
      /* if the previous operation is completed, proceed to erase the sector */
      FLASH->CR &= CR_PSIZE_MASK;
      FLASH->CR |= tmp_psize;
      FLASH->CR &= SECTOR_MASK;
      FLASH->CR |= FLASH_CR_SER | FLASH_Sector;
      FLASH->CR |= FLASH_CR_STRT;
      
      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastOperation();
      
      /* if the erase operation is completed, disable the SER Bit */
      FLASH->CR &= (~FLASH_CR_SER);
      FLASH->CR &= SECTOR_MASK; 
    }
  }
  /* Return the Erase Status */
  return status;
}


/**
  * @brief  Programs a half word (16-bit) at a specified address. 
  * @note   This function must be used when the device voltage range is from 2.1V to 3.6V. 
  *
  * @note   If an erase and a program operations are requested simultaneously,    
  *         the erase operation is performed before the program one.
  * 
  * @param  Address: specifies the address to be programmed.
  *         This parameter can be any address in Program memory zone or in OTP zone.  
  * @param  Data: specifies the data to be programmed.
  * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PROGRAM,
  *                       FLASH_ERROR_WRP, FLASH_ERROR_OPERATION or FLASH_COMPLETE.
  */
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
  FLASH_Status status = FLASH_COMPLETE;

  /* Check the parameters */
  if (IS_FLASH_ADDRESS(Address))
  {

    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation();
    
    if(status == FLASH_COMPLETE)
    {
      /* if the previous operation is completed, proceed to program the new data */
      FLASH->CR &= CR_PSIZE_MASK;
      FLASH->CR |= FLASH_PSIZE_HALF_WORD;
      FLASH->CR |= FLASH_CR_PG;
    
      *(__IO uint16_t*)Address = Data;
          
      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastOperation();

      /* if the program operation is completed, disable the PG Bit */
      FLASH->CR &= (~FLASH_CR_PG);
    } 
  }
    /* Return the Program Status */
  return status;
}


/**
  * @brief  Programs a word (32-bit) at a specified address.
  *
  * @note   This function must be used when the device voltage range is from 2.7V to 3.6V. 
  *
  * @note   If an erase and a program operations are requested simultaneously,    
  *         the erase operation is performed before the program one.
  *  
  * @param  Address: specifies the address to be programmed.
  *         This parameter can be any address in Program memory zone or in OTP zone.  
  * @param  Data: specifies the data to be programmed.
  * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PROGRAM,
  *                       FLASH_ERROR_WRP, FLASH_ERROR_OPERATION or FLASH_COMPLETE.
  */
FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data)
{
  FLASH_Status status = FLASH_COMPLETE;

  /* Check the parameters */
  if (IS_FLASH_ADDRESS(Address))
  {

    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation();
    
    if(status == FLASH_COMPLETE)
    {
      /* if the previous operation is completed, proceed to program the new data */
      FLASH->CR &= CR_PSIZE_MASK;
      FLASH->CR |= FLASH_PSIZE_WORD;
      FLASH->CR |= FLASH_CR_PG;
    
      *(__IO uint32_t*)Address = Data;
          
      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastOperation();

      /* if the program operation is completed, disable the PG Bit */
      FLASH->CR &= (~FLASH_CR_PG);
    }
  } 
  /* Return the Program Status */
  return status;
}

/**
  * @brief  Waits for a FLASH operation to complete.
  * @param  None
  * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PROGRAM,
  *                       FLASH_ERROR_WRP, FLASH_ERROR_OPERATION or FLASH_COMPLETE.
  */
FLASH_Status FLASH_WaitForLastOperation(void)
{ 
  __IO FLASH_Status status = FLASH_COMPLETE;
   
  /* Check for the FLASH Status */
  status = FLASH_GetStatus();

  /* Wait for the FLASH operation to complete by polling on BUSY flag to be reset.
     Even if the FLASH operation fails, the BUSY flag will be reset and an error
     flag will be set */
  while(status == FLASH_BUSY)
  {
    status = FLASH_GetStatus();
  }
  /* Return the operation status */
  return status;
}


/**
  * @brief  Clears the FLASH's pending flags.
  * @param  FLASH_FLAG: specifies the FLASH flags to clear.
  *          This parameter can be any combination of the following values:
  *            @arg FLASH_FLAG_EOP: FLASH End of Operation flag 
  *            @arg FLASH_FLAG_OPERR: FLASH operation Error flag 
  *            @arg FLASH_FLAG_WRPERR: FLASH Write protected error flag 
  *            @arg FLASH_FLAG_PGAERR: FLASH Programming Alignment error flag 
  *            @arg FLASH_FLAG_PGPERR: FLASH Programming Parallelism error flag
  *            @arg FLASH_FLAG_PGSERR: FLASH Programming Sequence error flag
  *            @arg FLASH_FLAG_RDERR: FLASH Read Protection error flag (STM32F42xx/43xxx and STM32F401xx/411xE devices)   
  * @retval None
  */
void FLASH_ClearFlag(uint32_t FLASH_FLAG)
{
  /* Check the parameters */
  if(IS_FLASH_CLEAR_FLAG(FLASH_FLAG))
  {
      /* Clear the flags */
      FLASH->SR = FLASH_FLAG;
  } else {
    printf("Clear flag error !\n");
  }
  return ;
}



/**
  * @brief  Returns the FLASH Status.
  * @param  None
  * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PROGRAM,
  *                       FLASH_ERROR_WRP, FLASH_ERROR_RD, FLASH_ERROR_OPERATION or FLASH_COMPLETE.
  */
FLASH_Status FLASH_GetStatus(void)
{
  FLASH_Status flashstatus = FLASH_COMPLETE;
  
  if((FLASH->SR & FLASH_FLAG_BSY) == FLASH_FLAG_BSY) 
  {
    flashstatus = FLASH_BUSY;
  }
  else 
  {  
    if((FLASH->SR & FLASH_FLAG_WRPERR) != (uint32_t)0x00)
    { 
      flashstatus = FLASH_ERROR_WRP;
    }
    else
    {
      if((FLASH->SR & FLASH_FLAG_RDERR) != (uint32_t)0x00)
      { 
        flashstatus = FLASH_ERROR_RD;
      } 
      else 
      {
        if((FLASH->SR & (uint32_t)0xE0) != (uint32_t)0x00)
        {
          flashstatus = FLASH_ERROR_PROGRAM; 
        }
        else
        {
          if((FLASH->SR & FLASH_FLAG_OPERR) != (uint32_t)0x00)
          {
            flashstatus = FLASH_ERROR_OPERATION;
          }
          else
          {
            flashstatus = FLASH_COMPLETE;
          }
        }
      }
    }
  }
  /* Return the FLASH Status */
  return flashstatus;
}

/**
  * @brief  Unlocks the FLASH control register access
  * @param  None
  * @retval None
  */
void FLASH_Unlock(void)
{
  if((FLASH->CR & FLASH_CR_LOCK) != RESET)
  {
    /* Authorize the FLASH Registers access */
    FLASH->KEYR = FLASH_KEY1;
    FLASH->KEYR = FLASH_KEY2;
  }  
}

/**
  * @brief  Locks the FLASH control register access
  * @param  None
  * @retval None
  */
void FLASH_Lock(void)
{
  /* Set the LOCK Bit to lock the FLASH Registers access */
  FLASH->CR |= FLASH_CR_LOCK;
}


FErrCode Flash_write(uint32_t addr, const uint32_t *buf, size_t size)
{
    FErrCode result = F_NO_ERR;
    size_t i;
    uint32_t read_data;

    if ( ! ((size % 4) == 0) )
    {
        printf("size : %d, not valid!\n", size);
        return F_WRITE_ERR;
    }

    FLASH_Unlock();
    FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR
                    | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
    for (i = 0; i < size; i += 4, buf++, addr += 4) {
        /* write data */
        FLASH_ProgramWord(addr, *buf);
        read_data = *(uint32_t *)addr;
        /* check data */
        if (read_data != *buf) {
            result = F_WRITE_ERR;
            break;
        }
    }
    FLASH_Lock();

    return result;
}


FErrCode Flash_read(uint32_t addr, uint32_t *buf, size_t size)
{
    FErrCode result = F_NO_ERR;

    if ( ! ((size % 4) == 0) )
    {
        printf("size : %d, not valid!\n", size);
        return F_READ_ERR;
    }

    /*copy from flash to ram */
    for (; size > 0; size -= 4, addr += 4, buf++) {
        *buf = *(uint32_t *) addr;
    }

    return result;
}

FErrCode Flash_erase(uint32_t addr, size_t size)
{
    FErrCode result = F_NO_ERR;
    FLASH_Status flash_status;
    size_t erased_size = 0;
    uint32_t cur_erase_sector;

    /* make sure the start address is a multiple of F_ERASE_MIN_SIZE */
    //EF_ASSERT(addr % F_ERASE_MIN_SIZE == 0);
    if ( addr % F_ERASE_MIN_SIZE )
    {
      printf("Erase start addr %d error :\n", addr);
      return F_ERASE_ERR;
    }

    /* start erase */
    FLASH_Unlock();
    FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR
                    | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
    /* it will stop when erased size is greater than setting size */
    while(erased_size < size) {
        cur_erase_sector = Flash_get_sector(addr + erased_size);
        flash_status = FLASH_EraseSector(cur_erase_sector, VoltageRange_3);
        if (flash_status != FLASH_COMPLETE) {
            result = F_ERASE_ERR;
            break;
        }
        erased_size += Flash_get_sector_size(cur_erase_sector);
    }
    FLASH_Lock();

    return result;
}


static uint32_t Flash_get_sector(uint32_t address)
{
    uint32_t sector = 0;

    if ((address < ADDR_FLASH_SECTOR_1) && (address >= ADDR_FLASH_SECTOR_0)) {
        sector = FLASH_Sector_0;
    } else if ((address < ADDR_FLASH_SECTOR_2) && (address >= ADDR_FLASH_SECTOR_1)) {
        sector = FLASH_Sector_1;
    } else if ((address < ADDR_FLASH_SECTOR_3) && (address >= ADDR_FLASH_SECTOR_2)) {
        sector = FLASH_Sector_2;
    } else if ((address < ADDR_FLASH_SECTOR_4) && (address >= ADDR_FLASH_SECTOR_3)) {
        sector = FLASH_Sector_3;
    } else if ((address < ADDR_FLASH_SECTOR_5) && (address >= ADDR_FLASH_SECTOR_4)) {
        sector = FLASH_Sector_4;
    } else if ((address < ADDR_FLASH_SECTOR_6) && (address >= ADDR_FLASH_SECTOR_5)) {
        sector = FLASH_Sector_5;
    } else if ((address < ADDR_FLASH_SECTOR_7) && (address >= ADDR_FLASH_SECTOR_6)) {
        sector = FLASH_Sector_6;
    } else if ((address < ADDR_FLASH_SECTOR_8) && (address >= ADDR_FLASH_SECTOR_7)) {
        sector = FLASH_Sector_7;
    } else if ((address < ADDR_FLASH_SECTOR_9) && (address >= ADDR_FLASH_SECTOR_8)) {
        sector = FLASH_Sector_8;
    } else if ((address < ADDR_FLASH_SECTOR_10) && (address >= ADDR_FLASH_SECTOR_9)) {
        sector = FLASH_Sector_9;
    } else if ((address < ADDR_FLASH_SECTOR_11) && (address >= ADDR_FLASH_SECTOR_10)) {
        sector = FLASH_Sector_10;
    } else if ((address < ADDR_FLASH_SECTOR_12) && (address >= ADDR_FLASH_SECTOR_11)) {
        sector = FLASH_Sector_11;
    } else if ((address < ADDR_FLASH_SECTOR_13) && (address >= ADDR_FLASH_SECTOR_12)) {
        sector = FLASH_Sector_12;
    } else if ((address < ADDR_FLASH_SECTOR_14) && (address >= ADDR_FLASH_SECTOR_13)) {
        sector = FLASH_Sector_13;
    } else if ((address < ADDR_FLASH_SECTOR_15) && (address >= ADDR_FLASH_SECTOR_14)) {
        sector = FLASH_Sector_14;
    } else if ((address < ADDR_FLASH_SECTOR_16) && (address >= ADDR_FLASH_SECTOR_15)) {
        sector = FLASH_Sector_15;
    } else if ((address < ADDR_FLASH_SECTOR_17) && (address >= ADDR_FLASH_SECTOR_16)) {
        sector = FLASH_Sector_16;
    } else if ((address < ADDR_FLASH_SECTOR_18) && (address >= ADDR_FLASH_SECTOR_17)) {
        sector = FLASH_Sector_17;
    } else if ((address < ADDR_FLASH_SECTOR_19) && (address >= ADDR_FLASH_SECTOR_18)) {
        sector = FLASH_Sector_18;
    } else if ((address < ADDR_FLASH_SECTOR_20) && (address >= ADDR_FLASH_SECTOR_19)) {
        sector = FLASH_Sector_19;
    } else if ((address < ADDR_FLASH_SECTOR_21) && (address >= ADDR_FLASH_SECTOR_20)) {
        sector = FLASH_Sector_20;
    } else if ((address < ADDR_FLASH_SECTOR_22) && (address >= ADDR_FLASH_SECTOR_21)) {
        sector = FLASH_Sector_21;
    } else if ((address < ADDR_FLASH_SECTOR_23) && (address >= ADDR_FLASH_SECTOR_22)) {
        sector = FLASH_Sector_22;
    } else /*(address < FLASH_END_ADDR) && (address >= ADDR_FLASH_SECTOR_23))*/
    {
        sector = FLASH_Sector_23;
    }

    return sector;
}

static uint32_t Flash_get_sector_size(uint32_t sector)
{
    if( ! IS_FLASH_SECTOR(sector))
    {
      printf("Sector %d is not a valid sector!\n", sector);
      return 0;
    }

    switch (sector) {
    case 0: return 16 * 1024;
    case 1: return 16 * 1024;
    case 2: return 16 * 1024;
    case 3: return 16 * 1024;
    case 4: return 64 * 1024;
    case 5: return 128 * 1024;
    case 6: return 128 * 1024;
    case 7: return 128 * 1024;
    case 8: return 128 * 1024;
    case 9: return 128 * 1024;
    case 10: return 128 * 1024;
    case 11: return 128 * 1024;
    case 12: return 16 * 1024;
    case 13: return 16 * 1024;
    case 14: return 16 * 1024;
    case 15: return 16 * 1024;
    case 16: return 64 * 1024;
    case 17: return 128 * 1024;
    case 18: return 128 * 1024;
    case 19: return 128 * 1024;
    case 20: return 128 * 1024;
    case 21: return 128 * 1024;
    case 22: return 128 * 1024;
    case 23: return 128 * 1024;
    default : return 128 * 1024;
    }

}

void Flash_ShowInfo(uint32_t *buf, size_t size)
{
    uint32_t i ;
    for(i = 0; i < size/(sizeof(uint32_t)); i++)
    {
      if(i == 0){
        printf("%d", *(buf+i));
      }else{
        printf("-%d", *(buf+i));
      }
      
    }
    printf("\n");
}

void FLASH_Status_String(FLASH_Status status, char * status_str)
{
  switch (status) {
    case FLASH_BUSY: strcpy(status_str, "FLASH_BUSY"); break;
    case FLASH_ERROR_RD: strcpy(status_str, "FLASH_ERROR_RD"); break;
    case FLASH_ERROR_PGS: strcpy(status_str, "FLASH_ERROR_PGS"); break;
    case FLASH_ERROR_PGP: strcpy(status_str, "FLASH_ERROR_PGP"); break;
    case FLASH_ERROR_PGA: strcpy(status_str, "FLASH_ERROR_PGA"); break;
    case FLASH_ERROR_WRP: strcpy(status_str, "FLASH_ERROR_WRP"); break;
    case FLASH_ERROR_PROGRAM: strcpy(status_str, "FLASH_ERROR_PROGRAM"); break;
    case FLASH_ERROR_OPERATION: strcpy(status_str, "FLASH_ERROR_OPERATION"); break;
    case FLASH_COMPLETE: strcpy(status_str, "FLASH_COMPLETE"); break;
    default: strcpy(status_str, "nonrecognition"); break;
  }
  
}

uint32_t Flash_available_size()
{
  return ADDR_FLASH_USER_SIZE;
}